<?php

class TokopediaClient {

    public $authUrl = 'https://accounts.tokopedia.com/token';
    public $apiRoot = "https://fs.tokopedia.net";
    public $appKey;
    public $appSecret;
    public $connectTimeout = 0;
    public $readTimeout = 0;
    
    public $debugMode = false;

    /**
     * 获取 Authorization
     * 
     * @param type $forceRenew
     * @return string
     * @throws Exception
     */
    private function _getAuthorization($forceRenew = false) {

        if (!$this->appKey || !$this->appSecret) {
            throw new Exception('app client not set');
        }
        $key = $this->appKey . ':' . $this->appSecret;
        $cache = PlatformAPIClient::getCacheHandler();

        // try get from cache
        if (!$forceRenew) {
            $data = $cache->get($key);
            if ($data) {
                return $data;
            }
        }

        // generate
        $url = $this->authUrl;
        $header = array(
            'Authorization: Basic ' . base64_encode($key),
        );
        $param = array(
            'grant_type' => 'client_credentials',
        );
        $ret = $this->_curl($url, $param, $header);
        $data = json_decode($ret, true);
        if (!$ret || !$data) {
            throw new Exception('bad return of authorization');
        }
        if (!empty($data['error'])) {
            throw new Exception('error get authorization: '.$data['error_description']);
        }
        $auth = $data['token_type'] . ' ' . $data['access_token'];

        // 设置到 缓存（提前十分钟过期）
        $cache->set($key, $auth, $data['expires_in'] - 600);

        return $auth;
    }

    /**
     * curl 请求
     * @param type $url
     * @param type $postFields
     * @param type $httpHeaders
     * @return type
     * @throws Exception
     */
    private function _curl($url, $postFields = null, $httpHeaders = null) {

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_FAILONERROR, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        if ($this->readTimeout) {
            curl_setopt($ch, CURLOPT_TIMEOUT, $this->readTimeout);
        }
        if ($this->connectTimeout) {
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->connectTimeout);
        }
        //https request
        if (strlen($url) > 5 && strtolower(substr($url, 0, 5)) == "https") {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        }

        // body
        if (is_array($postFields) && 0 < count($postFields)) {
            $postBodyString = '';
            $postMultipart = false;
            foreach ($postFields as $k => $v) {
                if ('@' != substr($v, 0, 1)) { //check whether file upload or not
                    $postBodyString .= "$k=" . urlencode($v) . "&";
                } else {// for file upload, set to multipart/form-data£¬else set to www-form-urlencoded
                    $postMultipart = true;
                }
            }
            unset($k, $v);
            $postBodyString = trim($postBodyString, '&');
            curl_setopt($ch, CURLOPT_POST, true);
            if ($postMultipart) {
                //There is a new Variable included with curl in PHP 5.5: 
                //CURLOPT_SAFE_UPLOAD this is set to false by default in PHP 5.5 and is switched to a default of true in PHP 5.6.
                curl_setopt($ch, CURLOPT_SAFE_UPLOAD, false);
            }
            curl_setopt($ch, CURLOPT_POSTFIELDS, $postBodyString);
        } elseif ($postFields) {
            $postBodyString = trim($postFields);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $postBodyString);
        }
        
        // header
        if (is_array($httpHeaders) && 0 < count($httpHeaders)) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeaders);
        }
        
        $response = curl_exec($ch);

        // debug info start //
        if ($this->debugMode) {
            echo '----- curl call -----', PHP_EOL;
            echo 'url: ', $url, PHP_EOL;
            if (!empty($postBodyString)) {
                echo 'body: ', $postBodyString, PHP_EOL;
            }
            if (!empty($httpHeaders)) {
                echo 'header: ', implode('; ', $httpHeaders), PHP_EOL;
            }
            echo 'response: ', $response, PHP_EOL, PHP_EOL;
        }
        // debug info end //

        if (curl_errno($ch)) {
            throw new Exception(curl_error($ch), 0);
        } else {
            $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            if (200 !== $httpStatusCode) {
                throw new Exception($response, $httpStatusCode);
            }
        }
        curl_close($ch);
        return $response;
    }

    /**
     * 调用接口
     * @param type $api
     * @param type $param
     * @param type $maxRetry
     * @return type
     */
    public function call($api, $param = [], $maxRetry = 1) {

        try {
            $requestUrl = $this->apiRoot . $api;
            $header = array(
                'Authorization: '.$this->_getAuthorization(),
            );
            // param
            if ($param) {
                $header[] = 'Content-Type: application/json';
                $param = json_encode($param);
            }
            $resp = $this->_curl($requestUrl, $param, $header);
            return $resp;
        } catch (Exception $e) {
            
            // 无授权
            if ($e->getCode() === 401 && $maxRetry > 0) {
                // 强制重新授权
                $this->_getAuthorization(true);
                $maxRetry--;
                return $this->call($api, $param, $maxRetry);
            }

            throw new Exception('call api failed:' . $e->getMessage());
        }
    }
}